#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>

/**
 *  学习1: 合成宏和分解宏使用
 *  学习2： 
 *   在应用中调用ioctl，驱动中执行对应的ioctl解析命令测试
 *     使用ioctl  读写数据
 *     使用ioctl 写结构体
 */


//  命令如何拼接：
//  Linux提供了宏 ，用来合成命令
// _IO(type,nr)    ：合成没有数据传递的命令
// _IOR(type,nr,size) :合成从驱动中读取数据的命令  ,size只能写类型
// _IOW(type,nr,size) ：合成向驱动中写数据的命令
// _IOWR(type,nr,size) ： 合成向驱动中首先写入在读取命令


//这里合成下面测试分解：
#define CMD_TEST0 _IO('A',0)
#define CMD_TEST1 _IOW('A',1,int)  //写
#define CMD_TEST2 _IOR('A',2,int)   //读
#define CMD_TESTB4 _IOWR('A',3,int)   //可读可写,这个命令要有要求的，首先写入，在读
#define CMD_TESTB4 _IOWR('A',3,int)  
#define CMD_TESTB5 _IOW('A',4,int)  

struct args{
	int a;
	int b;
	int c;
};


int main(int argc,char *argv[]){
  struct args test={1,2,3};

// 	分解宏： 测试
// _IOC_DIR(nr)  :分解方向
// _IOC_TYPE(nr) :  设备类型
// _IOC_NR(nr) :    设备序列号
// _IOC_SIZE(nr) :  大小

// 比如下面，led1 0 命令 写 开
// Led1#0#W#on 
// Led1#1#W#off 

  printf("type: %ld\n",_IOC_TYPE(CMD_TESTB4));     //类型
  printf("dir: %ld\n",_IOC_DIR(CMD_TESTB4));     //方向
  printf("nr: %ld\n",_IOC_NR(CMD_TESTB4));       //序号
  printf("size: %ld\n",_IOC_SIZE(CMD_TESTB4));   //大小
/**
type: 66   ASCII码  
dir: 3     方向：   2bit   只读(10)/只写(01)/读写(11)/无数据(00)    
nr: 3 
size: 4 
*/

#if 1
	int fd;//定义int类型的文件描述符fd
	int val;//定义int类型的传递参数val
	fd = open("/dev/test",O_RDWR);//打开test设备节点
	if(fd < 0){
		printf("file open fail\n");
	}
	if(!strcmp(argv[1], "write")){
		ioctl(fd,CMD_TEST1,1);//如果第二个参数为write，向内核空间写入1
	}
	else if(!strcmp(argv[1], "read")){
		ioctl(fd,CMD_TEST2,&val);//如果第二个参数为read，则读取内核空间传递向用户空间传递的值
		printf("val is %d\n",val);

    }
    
	ioctl(fd,CMD_TESTB5, &test);


	close(fd);
#endif 
}
